﻿#include "FITKRecogClosedSpace.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementTri.h"
#include <cmath>

namespace Interface
{
    FITKRecogClosedSpace::FITKRecogClosedSpace() 
    {
        m_mesh = new OpenVolumeMesh::GeometricPolyhedralMeshV3d;
    }
    FITKRecogClosedSpace::~FITKRecogClosedSpace() 
    {
        if (!m_mesh) delete m_mesh;
    }

    bool FITKRecogClosedSpace::initMesh(const QList<Core::FITKNode*>& meshPoints, const QList<Interface::FITKElementTri3*>& meshFaces)
    {
        if (meshPoints.empty() || meshFaces.empty()) return false;
        //对网格对象的数据清理
        m_mesh->clear();
        //记录网格点的容器
        std::vector<OpenVolumeMesh::VertexHandle> vhandles{};
        //记录面顶点的容器
        std::vector<OpenVolumeMesh::VertexHandle> face_vhandles{};
        //记录网格点的id
        QList<int> node_id{};

        //添加点
        for (QList<Core::FITKNode*>::const_iterator it = meshPoints.begin(); it != meshPoints.end(); it++) 
        {
            OpenVolumeMesh::Vec3d m_point = OpenVolumeMesh::Vec3d((*it)->x(), (*it)->y(), (*it)->z());
            //存储网格点的id
            node_id.append((*it)->getNodeID());
            vhandles.push_back(m_mesh->add_vertex(m_point));
        }

        //添加面
        for (QList<Interface::FITKElementTri3*>::const_iterator it = meshFaces.begin(); it != meshFaces.end(); ++it) 
        {
            face_vhandles.clear();
            //判断是否是三角形拓扑单元类型
            if ((*it)->getEleType() != FITKModelEnum::FITKEleType::Tri3) return false;

            //获取点的id,加入到面顶点的容器中
            for (int i = 0; i < (*it)->getNodeCount(); i++) 
            {
                //根据点的ID获取所在的索引
                int node_index = node_id.indexOf((*it)->getNodeID(i));
                if (node_index < 0 || node_index >= vhandles.size()) continue;
                face_vhandles.push_back(vhandles[node_index]);  
            }
            //添加面
            if (face_vhandles.size() != 3) continue;
            m_mesh->add_face(face_vhandles);
        }
        //提取网格对象的半面信息
        for (OpenVolumeMesh::HalfFaceIter it = m_mesh->halffaces_begin(); it != m_mesh->halffaces_end(); ++it)
        {
            m_allHalfFaces.insert(it->idx(), *it);
        }
        return true;
    }

    QList<QPair<int, int>> FITKRecogClosedSpace::getBoundary() 
    {
        QList<QPair<int, int>> boundary{};

        for (OpenVolumeMesh::EdgeIter edge_iter = m_mesh->e_iter(); edge_iter.valid(); ++edge_iter)
        {
            //自由边获取，输出自由边信息
            QList<OpenVolumeMesh::FaceHandle> adj_face{};

            for (OpenVolumeMesh::EdgeFaceIter aFace_iter = m_mesh->ef_iter(*edge_iter); aFace_iter.valid(); ++aFace_iter)
            {
                adj_face.append(*aFace_iter);
            }

			if (adj_face.size() != 1) continue;

            //获取单边的两个顶点
            std::array<OpenVolumeMesh::VertexHandle, 2> vertices = m_mesh->edge_vertices(*edge_iter);
            OpenVolumeMesh::VertexHandle vertex1 = vertices[0];
            OpenVolumeMesh::VertexHandle vertex2 = vertices[1];
            //存储单边的顶点索引
            QPair<int, int> me = QPair<int, int>(vertex1.idx(), vertex2.idx());
            boundary.append(me);
        }
        return boundary;
    }

	QList<double> FITKRecogClosedSpace::getSpaceVolume()
	{
		return m_spaceVolume;
	}

	QList<QList<int>> FITKRecogClosedSpace::getFacesIndex()
	{
		//存储封闭空间及组成封闭空间的面索引
		QList<QList<int>> allClosedSpaceFacesIndex{};

		if (m_allClosedSpaceHalfFaces.size() == 0) return allClosedSpaceFacesIndex;

		//输出组成封闭空间的面
		for (QHash<int, QList<OpenVolumeMesh::HalfFaceHandle>>::iterator face_it = m_allClosedSpaceHalfFaces.begin(); face_it != m_allClosedSpaceHalfFaces.end(); ++face_it)
		{
			QList<OpenVolumeMesh::HalfFaceHandle> cs_faces = face_it.value();
			QList<int> cs_faces_index{};

			if (cs_faces.size() == 0) continue;

			//存储封闭空间体积
			m_spaceVolume.append(calVolume(cs_faces));

			//把半面转化为面，并存储面的id
			for (QList<OpenVolumeMesh::HalfFaceHandle>::iterator it = cs_faces.begin(); it != cs_faces.end(); it++)
			{
				OpenVolumeMesh::FaceHandle cs_face = m_mesh->face_handle(*it);
				cs_faces_index.append(cs_face.idx());
			}
			//组成封闭空间的面id
			if (cs_faces_index.size() < 4) continue;
			allClosedSpaceFacesIndex.append(cs_faces_index);
		}
		return allClosedSpaceFacesIndex;
	}

    QList<int> FITKRecogClosedSpace::meshPreProcess()
    {
        //将筛选出的面存储到容器中
        QList<int> exc_face = {};
        bool has_singleEdge = true;

        while (has_singleEdge) 
        {
            has_singleEdge = false;

            for (OpenVolumeMesh::EdgeIter edge_iter = m_mesh->e_iter(); edge_iter.valid(); ++edge_iter)
            {
                //自由边获取，输出自由边信息
                QList<OpenVolumeMesh::FaceHandle> adj_face{};

                for (OpenVolumeMesh::EdgeFaceIter face_iter = m_mesh->ef_iter(*edge_iter); face_iter.valid(); face_iter++)
                {
                    adj_face.append(*face_iter);
                }

                if (adj_face.size() == 1) 
                {
                    //单边面的索引
                    exc_face.append(adj_face[0].idx());
                    //移除该面所对应的所有半面
                    m_allHalfFaces.remove(m_mesh->halfface_handle(adj_face[0], 0).idx());
                    m_allHalfFaces.remove(m_mesh->halfface_handle(adj_face[0], 1).idx());
                    //在网格中删除该面
                    m_mesh->delete_face(adj_face[0]);
                    has_singleEdge = true;
                }
            }
        }
        return exc_face;
    }

    void FITKRecogClosedSpace::recogClosedSpace(bool is_del) 
    {
        if (m_allHalfFaces.empty()) return;
        //记录所有封闭空间半面
        QList<QList<OpenVolumeMesh::HalfFaceHandle>> allClosedSpaceHalfFaces{};

        while (!m_allHalfFaces.empty()) 
        {
            //初始化种子半面
            OpenVolumeMesh::HalfFaceHandle seed_hf = m_allHalfFaces.constBegin().value();

            //将种子半面从所有半面中移除
            m_allHalfFaces.remove(seed_hf.idx());

            hfExpand(allClosedSpaceHalfFaces, seed_hf);
        }
        //删除多余的封闭空间
        if (is_del) this->delExtraSpace(allClosedSpaceHalfFaces);
    }

    void FITKRecogClosedSpace::hfExpand(QList<QList<OpenVolumeMesh::HalfFaceHandle>>& allclosedspace , const OpenVolumeMesh::HalfFaceHandle& seedhf)
    {
        //半面数组
        QList<OpenVolumeMesh::HalfFaceHandle> hf_array{};
        //半面队列
        QList<OpenVolumeMesh::HalfFaceHandle> hf_line{};
        //临时半面队列
        QList<OpenVolumeMesh::HalfFaceHandle> tem_hf_line{};
        //将种子半面加入队列
        hf_line.append(seedhf);
        hf_array.append(seedhf);
        while (!hf_line.empty()) 
        {
            //以队列中的半面为种子半面，识别相邻半面
            for (QList<OpenVolumeMesh::HalfFaceHandle>::iterator it = hf_line.begin(); it != hf_line.end(); it++) 
            {
                readNeighborHalfFace(*it, tem_hf_line);
            }
            //将队列中种子半面加入数组
            hf_array.append(tem_hf_line);
            //清空队列
            hf_line.clear();
            //将识别出的相邻半面加入队列
            hf_line.append(tem_hf_line);
            //清空临时队列
            tem_hf_line.clear();
        }
        //将封闭空间存储到容器中
        if (hf_array.size() <= m_mesh->n_halffaces() && hf_array.size() > 3) allclosedspace.append(hf_array);
    }

    void FITKRecogClosedSpace::readNeighborHalfFace(const OpenVolumeMesh::HalfFaceHandle& seedhf, QList<OpenVolumeMesh::HalfFaceHandle>& hfLine)
    {
        //种子面的法向量
        OpenVolumeMesh::Geometry::Vec3d sf_n = m_mesh->normal(seedhf);

        for (OpenVolumeMesh::HalfFaceHalfEdgeIter halfedge_iter = m_mesh->hfhe_iter(seedhf); halfedge_iter.valid(); ++halfedge_iter)
        {
            OpenVolumeMesh::HalfEdgeHandle opposite_halfedge = m_mesh->opposite_halfedge_handle(*halfedge_iter);

            //获取边的顶点
            //起点
            OpenVolumeMesh::VertexHandle point_idx0 = m_mesh->from_vertex_handle(opposite_halfedge);
            //终点
            OpenVolumeMesh::VertexHandle point_idx1 = m_mesh->to_vertex_handle(opposite_halfedge);

            //顶点坐标
            OpenVolumeMesh::Geometry::Vec3d c_point0 = m_mesh->vertex(point_idx0);
            OpenVolumeMesh::Geometry::Vec3d c_point1 = m_mesh->vertex(point_idx1);

            //半边向量
            OpenVolumeMesh::Geometry::Vec3d he_vec = c_point1 - c_point0;

            double face_angle = M_PI;
            OpenVolumeMesh::HalfFaceHandle half_face{};

            for (OpenVolumeMesh::HalfEdgeHalfFaceIter halfface_iter = m_mesh->hehf_iter(opposite_halfedge); halfface_iter.valid(); ++halfface_iter)
            {
				if (halfface_iter->idx() == m_mesh->opposite_halfface_handle(seedhf).idx()) continue;
                
                //相邻面的法向量
                OpenVolumeMesh::Geometry::Vec3d af_n = m_mesh->normal(*halfface_iter);

                //两半面之间的有向角
                double hf_angel = calAngle(he_vec, sf_n, af_n);

				if (hf_angel > face_angle) continue;

                face_angle = hf_angel;
                half_face = *halfface_iter;
            }

            //判断已识别半面是否存在于半面数组中
			if (!m_allHalfFaces.contains(half_face.idx())) continue;

            //将已识别半面从所有半面中移除
            m_allHalfFaces.remove(half_face.idx());
            hfLine.append(half_face);
        }
    }

    void FITKRecogClosedSpace::delExtraSpace(const QList<QList<OpenVolumeMesh::HalfFaceHandle>>& allclosedspace)
    {
        if (allclosedspace.size() == 0) return;
        //记录所有封闭空间的AABB包围盒长度
        QList<double> boundBoxsLength{};
        //获取所有封闭空间包围盒
		for (int i = 0; i < allclosedspace.size(); i++)
		{
			m_allClosedSpaceHalfFaces.insert(i, allclosedspace[i]);
			//记录封闭空间顶点
			QList<OpenVolumeMesh::Vec3d> closedSpaceVertex{};
			for (QList<OpenVolumeMesh::HalfFaceHandle>::const_iterator it = allclosedspace[i].begin(); it != allclosedspace[i].end(); it++)
			{
				//获取半面的顶点信息
				for (OpenVolumeMesh::HalfFaceVertexIter hfv_it = m_mesh->hfv_iter(*it); hfv_it.valid(); ++hfv_it)
				{
					//获取顶点坐标
					closedSpaceVertex.append(m_mesh->vertex(*hfv_it));
				}
			}
			//计算当前封闭空间的AABB包围盒
			OpenVolumeMesh::Vec3d maxPointa = OpenVolumeMesh::Vec3d(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity());
			OpenVolumeMesh::Vec3d minPointa = OpenVolumeMesh::Vec3d(-std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity());
			for (QList<OpenVolumeMesh::Vec3d>::iterator it = closedSpaceVertex.begin(); it != closedSpaceVertex.end(); it++)
			{
				minPointa[0] = std::min(minPointa[0], (*it)[0]);
				minPointa[1] = std::min(minPointa[1], (*it)[1]);
				minPointa[2] = std::min(minPointa[2], (*it)[2]);

				maxPointa[0] = std::max(maxPointa[0], (*it)[0]);
				maxPointa[1] = std::max(maxPointa[1], (*it)[1]);
				maxPointa[2] = std::max(maxPointa[2], (*it)[2]);
			}
			//包围盒对角线长度
			double AABBLength = (maxPointa - minPointa).length();
			//存储包围盒对角线长度
			boundBoxsLength.append(AABBLength);
		}

        //提取最大长度包围盒所对应的封闭空间索引
		if (m_allClosedSpaceHalfFaces.empty()) return;
 
        double max_length = -1;
        QList<int>max_length_idxs{};
        for (int i = 0; i < boundBoxsLength.size(); i++)
        {
            if (boundBoxsLength[i] < max_length) continue;
            if (boundBoxsLength[i] > max_length)
            {
                max_length = boundBoxsLength[i];
                //清空之前的索引
                max_length_idxs.clear();
                //添加当前索引
                max_length_idxs.append(i);
            }
            //如果值等于当前最大值，则追加当前索引
            else if (boundBoxsLength[i] == max_length) max_length_idxs.append(i);
        }

        if (max_length_idxs.size() == 1) m_allClosedSpaceHalfFaces.remove(max_length_idxs[0]);
        //存在多个包围盒相同的封闭空间，则采用体积法剔除
        if (max_length_idxs.size() >= 2)
        {
            //记录最大封闭空间体积及所在的索引
            double max_volume = -1;
            int max_volume_idx = -1;
            //计算每一个封闭空间体积
            for (int i = 0; i < max_length_idxs.size(); i++)
            {
                double volume = calVolume(allclosedspace[max_length_idxs[i]]);
					
                if (volume <= max_volume) continue;
                max_volume = volume;
                max_volume_idx = max_length_idxs[i];
            }
            //删除最外层封闭空间
            m_allClosedSpaceHalfFaces.remove(max_volume_idx);
        }
    }

    double FITKRecogClosedSpace::calAngle(OpenVolumeMesh::Geometry::Vec3d& hev, OpenVolumeMesh::Geometry::Vec3d& sfn, OpenVolumeMesh::Geometry::Vec3d& afn)
    {
        //种子面方向向量
        OpenVolumeMesh::Geometry::Vec3d sf_vector = sfn.cross(hev);

        //相邻面方向向量
        OpenVolumeMesh::Geometry::Vec3d af_vector = afn.cross(hev);

        //种子面向量与相邻面向量叉乘
        OpenVolumeMesh::Geometry::Vec3d tem_vector = sf_vector.cross(af_vector);

        //计算两面夹角
        double dot_asfn = sfn.dot(afn);
        double as_angle = std::acos(dot_asfn);

        //如果方向与半边方向相同，则返回角度为负值
        if (hev.dot(tem_vector) > 0) return -as_angle;
        else return as_angle;
    }

    double FITKRecogClosedSpace::calDistance(const OpenVolumeMesh::Geometry::Vec3d& point, const OpenVolumeMesh::HalfFaceHandle& halfface)
    {
        //半面的一个顶点
        OpenVolumeMesh::HalfFaceVertexIter it = m_mesh->hfv_iter(halfface);
        OpenVolumeMesh::Geometry::Vec3d halfface_point = m_mesh->vertex(*it);

        //法向量
        OpenVolumeMesh::Geometry::Vec3d hf_normal = m_mesh->normal(halfface);

        //返回点到面的距离
        return std::abs(hf_normal.dot(point - halfface_point));;
    }

    double FITKRecogClosedSpace::calVolume(const QList<OpenVolumeMesh::HalfFaceHandle>& halfface)
    {
        //总体积
        double totalVolume = 0.0;
        //基点
        OpenVolumeMesh::HalfFaceVertexIter it = m_mesh->hfv_iter(halfface[0]);
        //基点坐标
        OpenVolumeMesh::Geometry::Vec3d base_point = m_mesh->vertex(*it);

        for (QList<OpenVolumeMesh::HalfFaceHandle>::const_iterator it = halfface.begin(); it != halfface.end(); it++) 
        {
            if (calDistance(base_point, *it) == 0) continue;

            //记录半面顶点的容器
            QList<OpenVolumeMesh::Vec3d> hf_vhandles{};

            //获取半面的顶点信息
            for (OpenVolumeMesh::HalfFaceVertexIter hfv_it = m_mesh->hfv_iter(*it); hfv_it.valid(); ++hfv_it)
            {
                //获取顶点坐标
                hf_vhandles.append(m_mesh->vertex(*hfv_it));
            }

            //基点到面3个顶点的向量
            OpenVolumeMesh::Geometry::Vec3d normal0 = base_point - hf_vhandles[0];
            OpenVolumeMesh::Geometry::Vec3d normal1 = base_point - hf_vhandles[1];
            OpenVolumeMesh::Geometry::Vec3d normal2 = base_point - hf_vhandles[2];
            //体积公式1/6*a·(b x c)
            totalVolume += (1.0 / 6.0) * (normal0.dot(normal1.cross(normal2)));
        }
        return abs(totalVolume);
    }
}